unit Unit1; interface uses OleAuto, Unit2; { Smart Objects: Access to TFrmSample } {****************************************************************} { } { TurboCAD for Windows } { Copyright (c) 1993 - 1997 } { International Microcomputer Software, Inc. } { (IMSI) } { All rights reserved. } { } {****************************************************************} type TRoundedRect = class(TAutoObject) private { Private declarations } MyForm: TFrmSample; { Property Page form } function GetDescription: string; function GetClassID: string; automated { Automated declarations } { Smart Objects: Required properties and methods for Regen Methods } property Description: string read GetDescription; property ClassID: string read GetClassID; function GetEnumNames(PropID: Integer; var Names: Variant; var Values: Variant): Integer; function GetPageInfo(AGraphic: Variant; var StockPages: Integer; var Names: Variant): Integer; function GetPropertyInfo(var Names: Variant; var Types: Variant; var IDs: Variant; var Defaults: Variant): Integer; function GetWizardInfo(var Names: Variant): Integer; function Draw(GrfThis: Variant; View: Variant; mat: Variant) : WordBool; procedure OnGeometryChanged(Graphic: Variant; GeomID: Longint; paramOld: Variant; paramNew: Variant); function OnGeometryChanging(Graphic: Variant; GeomID: Integer; paramOld: Variant; paramNew: Variant): WordBool; function OnNewGraphic(grfThis: Variant; boolCopy: WordBool): WordBool; function OnCopyGraphic(grfCopy: Variant; grfSource: Variant): WordBool; procedure OnPropertyChanged(Graphic: Variant; PropID: Integer; OldValue: Variant; NewValue: Variant); function OnPropertyChanging(Graphic: Variant; PropID: Integer; OldValue: Variant; NewValue: Variant): WordBool; procedure OnPropertyGet(Graphic: Variant; PropID: Integer); function PageControls(ThisRegenMethod: Variant; Graphic: Variant; PageNumber: Integer; SaveProperties: WordBool): WordBool; procedure PageDone(ThisRegenMethod: Variant; PageNumber: Variant); function PropertyPages(ThisRegenMethod: Variant; PageNumber: Variant): WordBool; procedure Regen(grfThis: Variant); function Wizard(ThisRegenMethod: Variant; WizardNumber: Variant): WordBool; end; {$IFNDEF TARGET_EXE} { DLL Note: GetServerProgIDs is a required export for use by TurboCAD. } { It is needed because Delphi does not create type libraries. } function GetServerProgIDs(var ProgIDs: Variant) : Integer; { EXE Note: Comment these exports out if building an EXE Automation server. } { Required exports for .DLL servers and TurboCAD Delphi extensions. } exports DllGetClassObject, DllCanUnloadNow, DllRegisterServer, DllUnregisterServer, GetServerProgIDs; {$ENDIF} implementation uses SysUtils, Dialogs; { Required for StrToFloat, etc. } const { Smart Objects: Make AutoClassInfo accessible to other functions } { Needed because Delphi does not create type libraries } AutoClassInfo: TAutoClassInfo = ( AutoClass: TRoundedRect; ProgID: 'RRect.RoundedRect'; ClassID: '{4EA25981-A43C-11D0-A115-00A024158DAF}'; Description: 'Rounded Rectangle Example'; Instancing: acMultiInstance); { DBAPI constants } gkGraphic = 11; gkArc = 2; gkText = 6; gfCosmetic = 128; { Useful math constants } Pi: double = 3.14159265; { Special variant types } typeIntegerEnum = varSmallint + 100; typeLongEnum = varInteger + 100; typeStringEnum = varOleStr + 100; { Stock property pages } ppStockPen = 1; ppStockBrush = 2; ppStockText = 4; ppStockInsert = 8; ppStockViewport = 16; ppStockAuto = 32; { Property Ids } idRoundness = 1; { Property enums } { Number of properties, pages, wizards } NUM_PROPERTIES = 1; NUM_PAGES = 1; NUM_WIZARDS = 0; { TRoundedRect object methods } { Returns the user-visible description of this RegenMethod } function TRoundedRect.GetDescription: string; begin GetDescription := AutoClassInfo.Description; end; { Returns the persistent class id for this RegenMethod's property section } function TRoundedRect.GetClassID: string; begin GetClassID := AutoClassInfo.ClassID; end; { Retrieve types and names } function TRoundedRect.GetPropertyInfo(var Names: Variant; var Types: Variant; var IDs: Variant; var Defaults: Variant): Integer; begin try VarArrayRedim(Names, NUM_PROPERTIES); VarArrayRedim(Types, NUM_PROPERTIES); VarArrayRedim(IDs, NUM_PROPERTIES); VarArrayRedim(Defaults, NUM_PROPERTIES); Names[0] := 'Roundness'; Types[0] := varDouble; IDs[0] := idRoundness; Defaults[0] := 50.0; Result := NUM_PROPERTIES; except Result := 0; end; end; { Get the number of property pages supporting this RegenMethod } function TRoundedRect.GetPageInfo(AGraphic: Variant; var StockPages: Integer; var Names: Variant): Integer; begin VarArrayRedim(Names, NUM_PAGES); { Need the form } MyForm := TFrmSample.Create(Application); Names[0] := MyForm.Caption; MyForm.Free; StockPages := ppStockBrush + ppStockPen + ppStockAuto; GetPageInfo := NUM_PAGES; end; { Get the number of wizards supporting this RegenMethod } function TRoundedRect.GetWizardInfo(var Names: Variant): Integer; begin GetWizardInfo := NUM_WIZARDS; end; { Enumerate the names and values of a specified property } function TRoundedRect.GetEnumNames(PropID: Integer; var Names: Variant; var Values: Variant): Integer; begin GetEnumNames := 0; end; function TRoundedRect.PageControls(ThisRegenMethod: Variant; Graphic: Variant; PageNumber: Integer; SaveProperties: WordBool): WordBool; var Roundness: double; begin try if SaveProperties then begin { OK button on property page was clicked } { Form is still loaded } with MyForm do begin { Need try block for the case where you have } { TRoundedRect Turbo Shape and ahother "shape" selected } try { When the property page is closed, transfer the numeric } { roundness value from the EditBox to the Graphic } { Get the value as a double-precision number } Roundness := StrToFloat(txtRoundness.Text); { Make sure it's between 0 and 100 } if Roundness < 0.0 then Roundness := 0.0; if Roundness > 100.0 then Roundness := 100.0; { Set the roundness property value in the Graphic } Graphic.Properties['Roundness'] := Roundness; except end; end; end else begin { Property page is about to be opened } { Make sure the form is loaded } MyForm := TFrmSample.Create(Application); with MyForm do begin { If more than one TRoundedRect is selected and they do not } { have the same properties, don't set up this field } try { When the property page is opening, transfer the numeric } { roundness value from the Graphic to the TextBox } { Get the roundness property value from the Graphic } Roundness := Graphic.Properties['Roundness']; { Set the EditBox control's text } txtRoundness.Text := FloatToStrF(Roundness, ffGeneral, 3, 0); except end; end; end; PageControls := True; except { For debugging purposes, report that an error occurred } { Return false if an error occurred } PageControls := False; end; end; procedure TRoundedRect.PageDone(ThisRegenMethod: Variant; PageNumber: Variant); begin { Done with form } MyForm.Free; end; function TRoundedRect.PropertyPages(ThisRegenMethod: Variant; PageNumber: Variant): WordBool; var PageResult: Integer; begin with MyForm do begin PageResult := ShowModal; PropertyPages := (PageResult = mrOK); end; end; function TRoundedRect.Wizard(ThisRegenMethod: Variant; WizardNumber: Variant): WordBool; begin Wizard := False; end; { Called when vertex has been moved, or other geometry change } procedure TRoundedRect.OnGeometryChanged(Graphic: Variant; GeomID: Longint; paramOld: Variant; paramNew: Variant); begin { Do nothing } end; { Called when vertex is moved, or other geometry change } function TRoundedRect.OnGeometryChanging(Graphic: Variant; GeomID: Integer; paramOld: Variant; paramNew: Variant): WordBool; begin { OK to continue with change } OnGeometryChanging := True; end; function TRoundedRect.OnNewGraphic(grfThis: Variant; boolCopy: WordBool): WordBool; var R, Roundness, Offset: double; Vertices, vTrue, vFalse: Variant; X, Y, Z: double; begin if boolCopy then begin { Vertices are already added for us... } OnNewGraphic := True; exit; end; try { New Graphic being created } { Temporary veriable for Vertices.Add } Vertices := grfThis.Vertices; { Define True and False variants } vTrue := True; vFalse := False; { First Vertex is "lower left" corner } { Arguments for Vertices.Add are: { X, Y, Z: double; } { PenDown, Selectable, Snappable, Editable, Linkable, Calculated, } { Before, After: Variant. } { Specify all flags; Omit Before and After arguments. } X := -1.0; Y := -0.5; Z := 0.0; Vertices.Add(X, Y, Z, vFalse, vTrue, vFalse, vFalse, vFalse, vFalse, , ); { Second Vertex is "upper right" corner } X := 1.0; Y := 0.5; Vertices.Add(X, Y, Z, vFalse, vTrue, vFalse, vFalse, vFalse, vFalse, , ); { Third Vertex is rounding handle (calculated) } Roundness := grfThis.Properties['Roundness']; R := 0.5 * Roundness / 100.0; Offset := 0.1 * R; X := 1.0 - R; Y := 0.5 + Offset; Vertices.Add(X, Y, Z, vFalse, vFalse, vFalse, vFalse, vFalse, vFalse, , ); { Fourth Vertex is rounding handle (editable) } Vertices.Add(X, Y, Z, vFalse, vTrue, vFalse, vTrue, vFalse, vFalse, , ); OnNewGraphic := True; except { Return false on failure } OnNewGraphic := False; end; end; function TRoundedRect.OnCopyGraphic(grfCopy: Variant; grfSource: Variant): WordBool; begin { OK to proceed } OnCopyGraphic := True; end; { Notification function called after graphic property is saved } procedure TRoundedRect.OnPropertyChanged(Graphic: Variant; PropID: Integer; OldValue: Variant; NewValue: Variant); begin { Do nothing } end; { Notification function called when graphic property is saved } function TRoundedRect.OnPropertyChanging(Graphic: Variant; PropID: Integer; OldValue: Variant; NewValue: Variant): WordBool; begin { OK to proceed } OnPropertyChanging := True; end; { Notification function called when graphic property is retrieved } procedure TRoundedRect.OnPropertyGet(Graphic: Variant; PropID: Integer); begin { Do nothing } end; { Called when graphic's internal structure needs to be updated } procedure TRoundedRect.Regen(grfThis: Variant); var LockCount: Integer; boolHandleMoved: WordBool; W, H, R, Roundness: double; X, Y, Z, X0, Y0, X1, Y1, T, StartAngle, EndAngle: double; Props, propRoundness: Variant; grfChild, Vertices, V0, V1, V2, V3, vTrue, vFalse: Variant; begin { Setup error handler } try { grfThis.Application.PushVertexDefaults Editable:=True, Selectable:=True } { Set up lock (prevent recursion) } LockCount := grfThis.RegenLock; { Setup error handler (make sure lock is removed) } if LockCount = 0 then begin try { Delete any previous cosmetic children } grfThis.Graphics.Clear(gfCosmetic); { Calculate height, width and radius of corners } Vertices := grfThis.Vertices; V0 := Vertices.Item[0]; { First corner } V1 := Vertices.Item[1]; { Diagonal corner } V2 := Vertices.Item[2]; { Radius } V3 := Vertices.Item[3]; { Drag handle } if (Abs(V2.X - V3.X) < 0.000001) and (Abs(V2.Y - V3.Y) < 0.000001) then boolHandleMoved := False else boolHandleMoved := True; W := Abs(V1.X - V0.X); H := Abs(V1.Y - V0.Y); { Radius of arcs is based on minimum of width and height } if W < H then R := W / 2.0 else R := H / 2.0; { Adjust radius for roundness } Props := grfThis.Properties; propRoundness := Props.Item['Roundness']; if boolHandleMoved then begin Roundness := Abs(V2.X - V3.X); Roundness := Roundness * 100.0 / R; if Roundness > 100.0 then Roundness := 100.0; { Relocate handle } { Update property to reflect handle location } propRoundness.Value := Roundness; end else begin Roundness := propRoundness.Value; if Roundness < 0.0 then Roundness := 0.0; if Roundness > 100.0 then Roundness := 100.0; end; R := R * Roundness / 100.0; { Add child Graphics } X0 := V0.X; Y0 := V0.Y; X1 := V1.X; Y1 := V1.Y; { Make sure X0 < X1 } if X0 > X1 then begin T := X0; X0 := X1; X1 := T; end; { Make sure Y0 < Y1 } if Y0 > Y1 then begin T := Y0; Y0 := Y1; Y1 := T; end; vTrue := True; vFalse := False; if R = 0 then begin { No rounded corners } { All children are cosmetic } grfChild := grfThis.Graphics.Add( , , vTrue, , , ); grfChild.Cosmetic := True; { Now add vertices to the child } Vertices := grfChild.Vertices; X := X0; Y := Y0; Z := 0.0; Vertices.Add(X, Y, Z, , , , , , , , ); Y := Y1; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); X := X1; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); Y := Y0; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); { Close the rectangle } Vertices.AddClose(vTrue, , , , , ); end else begin { Rounded corners } { We'll make 4 line children and 4 arc children } { First line } { All children are cosmetic } grfChild := grfThis.Graphics.Add( , , vTrue, , , ); grfChild.Cosmetic := True; { Now add vertices to the child } Vertices := grfChild.Vertices; X := X0 + R; Y := Y0; Z := 0; Vertices.Add(X, Y, Z, , , , , , , , ); X := X1 - R; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); { First arc } grfChild := grfThis.Graphics.Add(gkArc, , vTrue, , , ); grfChild.Cosmetic := True; Y := Y0 + R; StartAngle := 1.5 * Pi; EndAngle := 0.0; grfChild.ArcSet(X, Y, Z, R, , StartAngle, EndAngle, ); { Second line } grfChild := grfThis.Graphics.Add( , , vTrue, , , ); grfChild.Cosmetic := True; Vertices := grfChild.Vertices; X := X1; Vertices.Add(X, Y, Z, , , , , , , , ); Y := Y1 - R; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); { Second arc } grfChild := grfThis.Graphics.Add(gkArc, , vTrue, , , ); grfChild.Cosmetic := True; X := X1 - R; StartAngle := 0.0; EndAngle := 0.5 * Pi; grfChild.ArcSet(X, Y, Z, R, , StartAngle, EndAngle, ); { Third line } grfChild := grfThis.Graphics.Add( , , vTrue, , , ); grfChild.Cosmetic := True; Vertices := grfChild.Vertices; Y := Y1; Vertices.Add(X, Y, Z, , , , , , , , ); X := X0 + R; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); { Third arc } grfChild := grfThis.Graphics.Add(gkArc, , vTrue, , , ); grfChild.Cosmetic := True; Y := Y1 - R; StartAngle := 0.5 * Pi; EndAngle := Pi; grfChild.ArcSet(X, Y, Z, R, , StartAngle, EndAngle, ); { Fourth line } grfChild := grfThis.Graphics.Add( , , vTrue, , , ); grfChild.Cosmetic := True; Vertices := grfChild.Vertices; X := X0; Vertices.Add(X, Y, Z, , , , , , , , ); Y := Y0 + R; Vertices.Add(X, Y, Z, vTrue, , , , , , , ); { Fourth arc } grfChild := grfThis.Graphics.Add(gkArc, , vTrue, , , ); grfChild.Cosmetic := True; X := X0 + R; StartAngle := Pi; EndAngle := 1.5 * Pi; grfChild.ArcSet(X, Y, Z, R, , StartAngle, EndAngle, ); end; { Add visible child Graphics } except end; end; { if LockCount = 0 } { Remove lock } grfThis.RegenUnlock; { grfThis.Application.PopVertexDefaults } except end; end; { Called to do special draw proocessing } function TRoundedRect.Draw(GrfThis: Variant; View: Variant; mat: Variant) : WordBool; begin { Return True if we did the redraw (no further processing necessary, } { no children will be drawn). } { Since this is just a test, we return False to let TurboCAD do the } { drawing operation. } Draw := False; end; {$IFNDEF TARGET_EXE} { DLL Note: GetServerProgIDs is a required function for TurboCAD extensions. } { EXE Note: Comment out GetServerProgIDs if you are building an EXE server, { and see the note below regarding required resources. } { In lieu of type library, we need to get the CLSID of the OleAuto } { object somehow. Once we have the CLSID, we can merrily call } { CoCreateInstance to get an object... } function GetServerProgIDs(var ProgIDs: Variant) : Integer; begin VarArrayRedim(ProgIDs, 1); { Redimension array } ProgIDs[0] := AutoClassInfo.ProgID; { Return ProgID in array element } GetServerProgIDs := 1; { Return size of array } end; {$ELSE} { EXE Note: When building an .EXE server, you should add a resource named } { "ProgIDs" of type RCDATA with the ProgID strings separated by NUL } { characters. For example, this server would contain a resource file } { generated from the following .RC file: } { ProgIDs RCDATA BEGIN "RRect.RoundedRect\0" END } { Save the script in a file called "ProgIds.rc". } { Compile ProgIds.rc using "Brc32.exe -r ProgIds.rc". } { Include the resulting .RES file in our project with the $RESOURCE directive. } { using Delphi's $RESOURCE directive in the DPR file. } {$ENDIF} procedure RegisterRoundedRect; begin Automation.RegisterClass(AutoClassInfo); end; initialization RegisterRoundedRect; end.
SDK Top API Reference TurboCAD Home Page TurboCAD Programming Forums